package com.thinkingcoder.tccache.core.operator;

import java.util.Arrays;
import java.util.LinkedHashSet;

import com.thinkingcoder.tccache.core.model.CacheEntry;
import com.thinkingcoder.tccache.core.model.ZSetEntry;

/**
 * ZSet operator.
 */
public class ZSetOperator extends AbstractOperator {
    public Integer zAdd(String key, String[] values, double[] scores) {
        CacheEntry<?> cacheEntry = getOrInitCacheEntry(key);
        LinkedHashSet<ZSetEntry> exist = (LinkedHashSet<ZSetEntry>)cacheEntry.getValue();
        for (int i = 0; i < values.length; i++) {
            exist.add(new ZSetEntry(values[i], scores[i]));
        }
        return values.length;
    }

    public Integer zCard(String key) {
        if (checkInvalid(key)) {
            return 0;
        }

        LinkedHashSet<?> exist = getZSetEntryValue(key);
        return exist == null ? 0 : exist.size();
    }

    public Integer zCount(String key, double min, double max) {
        if (checkInvalid(key)) {
            return 0;
        }
        LinkedHashSet<ZSetEntry> exist = getZSetEntryValue(key);
        return exist == null ? 0 : (int)exist.stream().filter(x -> x.getScore() >= min && x.getScore() <= max).count();
    }

    public Double zScore(String key, String val) {
        if (checkInvalid(key)) {
            return null;
        }

        LinkedHashSet<ZSetEntry> exist = getZSetEntryValue(key);
        return exist == null ? null
            : exist.stream().filter(x -> x.getValue().equals(val)).map(ZSetEntry::getScore).findFirst().orElse(null);
    }

    public Integer zRank(String key, String val) {
        if (checkInvalid(key)) {
            return null;
        }
        LinkedHashSet<ZSetEntry> exist = getZSetEntryValue(key);
        if (exist == null) {
            return null;
        }
        Double zScore = zScore(key, val);
        if (zScore == null) {
            return null;
        }
        return (int)exist.stream().filter(x -> x.getScore() < zScore).count();
    }

    public Integer zRem(String key, String[] values) {
        if (checkInvalid(key) || values == null || values.length == 0) {
            return 0;
        }

        LinkedHashSet<ZSetEntry> exist = getZSetEntryValue(key);
        return exist == null ? 0
            : (int)Arrays.stream(values).map(x -> exist.removeIf(y -> y.getValue().equals(x))).filter(x -> x).count();
    }

    private CacheEntry<?> getOrInitCacheEntry(String key) {
        CacheEntry<?> entry = getCacheEntry(key);
        if (entry == null) {
            synchronized (getMap()) {
                if ((entry = getCacheEntry(key)) == null) {
                    entry = new CacheEntry<>(new LinkedHashSet<String>(), System.currentTimeMillis(), -1000);
                    putCacheEntry(key, entry);
                }
            }
        }
        return entry;
    }

    private LinkedHashSet<ZSetEntry> getZSetEntryValue(String key) {
        return (LinkedHashSet<ZSetEntry>)getCacheEntryValue(key);
    }
}
